<!DOCTYPE html>
<html>
<head>
    <title>GeoJSON几何类型坐标结构对比</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            line-height: 1.6;
        }
        .geometry-type {
            margin: 20px 0;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 8px;
            background: #f9f9f9;
        }
        .geometry-type h3 {
            color: #333;
            margin-top: 0;
            border-bottom: 2px solid #007acc;
            padding-bottom: 5px;
        }
        .structure {
            background: #fff;
            padding: 15px;
            border-left: 4px solid #007acc;
            margin: 10px 0;
            font-family: monospace;
            white-space: pre-wrap;
        }
        .example {
            background: #e8f4fd;
            padding: 15px;
            border-radius: 4px;
            margin: 10px 0;
        }
        .visualization {
            display: flex;
            gap: 20px;
            margin: 15px 0;
        }
        .canvas-container {
            flex: 1;
        }
        canvas {
            border: 1px solid #ccc;
            width: 100%;
            max-width: 300px;
        }
        .info {
            flex: 1;
            font-size: 14px;
        }
        .dimension-note {
            background: #fff3cd;
            border: 1px solid #ffeaa7;
            padding: 10px;
            border-radius: 4px;
            margin: 10px 0;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
        }
        th {
            background: #f2f2f2;
        }
        .access-pattern {
            background: #e8f5e8;
            padding: 10px;
            border-radius: 4px;
            margin: 10px 0;
            font-family: monospace;
        }
    </style>
</head>
<body>
    <h1>GeoJSON几何类型坐标结构详解</h1>
    <p>GeoJSON中不同几何类型的coordinates字段结构完全不同，理解这些差异对正确处理地理数据至关重要。</p>

    <div class="dimension-note">
        <strong>坐标维度说明：</strong>
        <ul>
            <li><strong>Position:</strong> [longitude, latitude] 或 [x, y]</li>
            <li><strong>Position with elevation:</strong> [longitude, latitude, elevation]</li>
            <li><strong>数组嵌套层级：</strong> 不同几何类型的坐标数组嵌套深度不同</li>
        </ul>
    </div>

    <div class="geometry-type">
        <h3>1. Point - 点</h3>
        <div class="structure">坐标结构: Position
coordinates: [longitude, latitude]</div>
        
        <div class="example">
            <strong>示例：</strong>
            <pre>{
  "type": "Point",
  "coordinates": [120.123, 30.456]
}</pre>
        </div>
        
        <div class="visualization">
            <div class="canvas-container">
                <canvas id="pointCanvas" width="200" height="150"></canvas>
            </div>
            <div class="info">
                <strong>特点：</strong>
                <ul>
                    <li>最简单的坐标结构</li>
                    <li>直接是一个二维数组</li>
                    <li>表示单个位置点</li>
                </ul>
            </div>
        </div>
        
        <div class="access-pattern">
访问方式:
const [lng, lat] = geometry.coordinates;
console.log(`经度: ${lng}, 纬度: ${lat}`);</div>
    </div>

    <div class="geometry-type">
        <h3>2. MultiPoint - 多点</h3>
        <div class="structure">坐标结构: Array&lt;Position&gt;
coordinates: [[lng1, lat1], [lng2, lat2], ...]</div>
        
        <div class="example">
            <strong>示例：</strong>
            <pre>{
  "type": "MultiPoint",
  "coordinates": [
    [120.123, 30.456],
    [121.234, 31.567],
    [119.876, 29.345]
  ]
}</pre>
        </div>
        
        <div class="visualization">
            <div class="canvas-container">
                <canvas id="multipointCanvas" width="200" height="150"></canvas>
            </div>
            <div class="info">
                <strong>特点：</strong>
                <ul>
                    <li>Point的数组</li>
                    <li>二维数组结构</li>
                    <li>表示多个独立的点</li>
                </ul>
            </div>
        </div>
        
        <div class="access-pattern">
访问方式:
geometry.coordinates.forEach(([lng, lat], index) => {
  console.log(`点${index + 1}: ${lng}, ${lat}`);
});</div>
    </div>

    <div class="geometry-type">
        <h3>3. LineString - 线段</h3>
        <div class="structure">坐标结构: Array&lt;Position&gt;
coordinates: [[lng1, lat1], [lng2, lat2], ...]</div>
        
        <div class="example">
            <strong>示例：</strong>
            <pre>{
  "type": "LineString",
  "coordinates": [
    [120.123, 30.456],
    [120.234, 30.567],
    [120.345, 30.678]
  ]
}</pre>
        </div>
        
        <div class="visualization">
            <div class="canvas-container">
                <canvas id="linestringCanvas" width="200" height="150"></canvas>
            </div>
            <div class="info">
                <strong>特点：</strong>
                <ul>
                    <li>与MultiPoint结构相同</li>
                    <li>但表示连续的路径</li>
                    <li>至少需要2个点</li>
                </ul>
            </div>
        </div>
        
        <div class="access-pattern">
访问方式:
const points = geometry.coordinates;
console.log(`起点: ${points[0]}`);
console.log(`终点: ${points[points.length - 1]}`);</div>
    </div>

    <div class="geometry-type">
        <h3>4. MultiLineString - 多线段</h3>
        <div class="structure">坐标结构: Array&lt;Array&lt;Position&gt;&gt;
coordinates: [
  [[lng1, lat1], [lng2, lat2], ...],  // 第一条线
  [[lng3, lat3], [lng4, lat4], ...],  // 第二条线
  ...
]</div>
        
        <div class="example">
            <strong>示例：</strong>
            <pre>{
  "type": "MultiLineString",
  "coordinates": [
    [[120.1, 30.1], [120.2, 30.2], [120.3, 30.3]],
    [[121.1, 31.1], [121.2, 31.2]]
  ]
}</pre>
        </div>
        
        <div class="visualization">
            <div class="canvas-container">
                <canvas id="multilinestringCanvas" width="200" height="150"></canvas>
            </div>
            <div class="info">
                <strong>特点：</strong>
                <ul>
                    <li>LineString的数组</li>
                    <li>三维数组结构</li>
                    <li>表示多条独立的线段</li>
                </ul>
            </div>
        </div>
        
        <div class="access-pattern">
访问方式:
geometry.coordinates.forEach((lineString, index) => {
  console.log(`线段${index + 1}有${lineString.length}个点`);
  lineString.forEach(([lng, lat]) => {
    console.log(`  点: ${lng}, ${lat}`);
  });
});</div>
    </div>

    <div class="geometry-type">
        <h3>5. Polygon - 多边形</h3>
        <div class="structure">坐标结构: Array&lt;Array&lt;Position&gt;&gt;
coordinates: [
  [[lng1, lat1], [lng2, lat2], ..., [lng1, lat1]],  // 外环(必须闭合)
  [[lng3, lat3], [lng4, lat4], ..., [lng3, lat3]],  // 内环1(可选)
  [[lng5, lat5], [lng6, lat6], ..., [lng5, lat5]]   // 内环2(可选)
]</div>
        
        <div class="example">
            <strong>示例：</strong>
            <pre>{
  "type": "Polygon",
  "coordinates": [
    // 外环
    [[120.0, 30.0], [121.0, 30.0], [121.0, 31.0], [120.0, 31.0], [120.0, 30.0]],
    // 内环(洞)
    [[120.2, 30.2], [120.8, 30.2], [120.8, 30.8], [120.2, 30.8], [120.2, 30.2]]
  ]
}</pre>
        </div>
        
        <div class="visualization">
            <div class="canvas-container">
                <canvas id="polygonCanvas" width="200" height="150"></canvas>
            </div>
            <div class="info">
                <strong>特点：</strong>
                <ul>
                    <li>三维数组结构</li>
                    <li>第一个环是外边界</li>
                    <li>后续环是内部洞孔</li>
                    <li>所有环必须闭合</li>
                </ul>
            </div>
        </div>
        
        <div class="access-pattern">
访问方式:
const [outerRing, ...holes] = geometry.coordinates;
console.log(`外环有${outerRing.length}个点`);
console.log(`有${holes.length}个洞`);</div>
    </div>

    <div class="geometry-type">
        <h3>6. MultiPolygon - 多多边形</h3>
        <div class="structure">坐标结构: Array&lt;Array&lt;Array&lt;Position&gt;&gt;&gt;
coordinates: [
  [  // 第一个多边形
    [[lng1, lat1], ...],  // 外环
    [[lng2, lat2], ...]   // 内环(可选)
  ],
  [  // 第二个多边形
    [[lng3, lat3], ...]   // 外环
  ]
]</div>
        
        <div class="example">
            <strong>示例：</strong>
            <pre>{
  "type": "MultiPolygon",
  "coordinates": [
    [
      // 第一个多边形的外环
      [[120.0, 30.0], [121.0, 30.0], [121.0, 31.0], [120.0, 31.0], [120.0, 30.0]]
    ],
    [
      // 第二个多边形的外环
      [[122.0, 32.0], [123.0, 32.0], [123.0, 33.0], [122.0, 33.0], [122.0, 32.0]]
    ]
  ]
}</pre>
        </div>
        
        <div class="visualization">
            <div class="canvas-container">
                <canvas id="multipolygonCanvas" width="200" height="150"></canvas>
            </div>
            <div class="info">
                <strong>特点：</strong>
                <ul>
                    <li>四维数组结构</li>
                    <li>Polygon的数组</li>
                    <li>表示多个独立的多边形</li>
                    <li>每个多边形可有洞孔</li>
                </ul>
            </div>
        </div>
        
        <div class="access-pattern">
访问方式:
geometry.coordinates.forEach((polygon, index) => {
  console.log(`多边形${index + 1}:`);
  const [outerRing, ...holes] = polygon;
  console.log(`  外环: ${outerRing.length}个点`);
  console.log(`  洞孔: ${holes.length}个`);
});</div>
    </div>

    <h2>坐标结构对比表</h2>
    <table>
        <thead>
            <tr>
                <th>几何类型</th>
                <th>坐标结构</th>
                <th>数组维度</th>
                <th>示例访问</th>
                <th>用途</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Point</td>
                <td>Position</td>
                <td>1维</td>
                <td>coords[0], coords[1]</td>
                <td>单个位置点</td>
            </tr>
            <tr>
                <td>MultiPoint</td>
                <td>Position[]</td>
                <td>2维</td>
                <td>coords[i][0], coords[i][1]</td>
                <td>多个独立点</td>
            </tr>
            <tr>
                <td>LineString</td>
                <td>Position[]</td>
                <td>2维</td>
                <td>coords[i][0], coords[i][1]</td>
                <td>连续路径</td>
            </tr>
            <tr>
                <td>MultiLineString</td>
                <td>Position[][]</td>
                <td>3维</td>
                <td>coords[i][j][0], coords[i][j][1]</td>
                <td>多条独立路径</td>
            </tr>
            <tr>
                <td>Polygon</td>
                <td>Position[][]</td>
                <td>3维</td>
                <td>coords[i][j][0], coords[i][j][1]</td>
                <td>单个面积区域</td>
            </tr>
            <tr>
                <td>MultiPolygon</td>
                <td>Position[][][]</td>
                <td>4维</td>
                <td>coords[i][j][k][0], coords[i][j][k][1]</td>
                <td>多个独立区域</td>
            </tr>
        </tbody>
    </table>

    <h2>处理不同几何类型的通用函数</h2>
    <div class="example">
        <pre>function processGeometry(geometry) {
  switch (geometry.type) {
    case 'Point':
      const [lng, lat] = geometry.coordinates;
      console.log(`点: ${lng}, ${lat}`);
      break;
      
    case 'MultiPoint':
    case 'LineString':
      geometry.coordinates.forEach(([lng, lat], i) => {
        console.log(`${geometry.type} 点${i}: ${lng}, ${lat}`);
      });
      break;
      
    case 'MultiLineString':
    case 'Polygon':
      geometry.coordinates.forEach((ring, i) => {
        console.log(`${geometry.type} 环${i}:`);
        ring.forEach(([lng, lat], j) => {
          console.log(`  点${j}: ${lng}, ${lat}`);
        });
      });
      break;
      
    case 'MultiPolygon':
      geometry.coordinates.forEach((polygon, i) => {
        console.log(`多边形${i}:`);
        polygon.forEach((ring, j) => {
          console.log(`  环${j}:`);
          ring.forEach(([lng, lat], k) => {
            console.log(`    点${k}: ${lng}, ${lat}`);
          });
        });
      });
      break;
  }
}</pre>
    </div>

    <script>
        // 绘制不同几何类型的示例
        function drawPoint() {
            const canvas = document.getElementById('pointCanvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            ctx.fillStyle = '#ff0000';
            ctx.beginPath();
            ctx.arc(100, 75, 5, 0, 2 * Math.PI);
            ctx.fill();
            
            ctx.fillStyle = '#000';
            ctx.font = '12px Arial';
            ctx.fillText('Point', 110, 80);
        }

        function drawMultiPoint() {
            const canvas = document.getElementById('multipointCanvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            const points = [[50, 50], [100, 80], [150, 60]];
            
            ctx.fillStyle = '#ff0000';
            points.forEach(([x, y], i) => {
                ctx.beginPath();
                ctx.arc(x, y, 4, 0, 2 * Math.PI);
                ctx.fill();
                
                ctx.fillStyle = '#000';
                ctx.font = '10px Arial';
                ctx.fillText(`P${i+1}`, x + 6, y - 6);
                ctx.fillStyle = '#ff0000';
            });
        }

        function drawLineString() {
            const canvas = document.getElementById('linestringCanvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            const points = [[30, 100], [80, 50], [130, 80], [170, 40]];
            
            // 绘制线段
            ctx.strokeStyle = '#0066cc';
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.moveTo(points[0][0], points[0][1]);
            points.slice(1).forEach(([x, y]) => {
                ctx.lineTo(x, y);
            });
            ctx.stroke();
            
            // 绘制点
            ctx.fillStyle = '#0066cc';
            points.forEach(([x, y]) => {
                ctx.beginPath();
                ctx.arc(x, y, 3, 0, 2 * Math.PI);
                ctx.fill();
            });
        }

        function drawMultiLineString() {
            const canvas = document.getElementById('multilinestringCanvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            const lines = [
                [[30, 50], [80, 30], [130, 50]],
                [[50, 100], [100, 80], [150, 100]]
            ];
            
            const colors = ['#0066cc', '#cc6600'];
            
            lines.forEach((line, i) => {
                ctx.strokeStyle = colors[i];
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.moveTo(line[0][0], line[0][1]);
                line.slice(1).forEach(([x, y]) => {
                    ctx.lineTo(x, y);
                });
                ctx.stroke();
                
                // 绘制点
                ctx.fillStyle = colors[i];
                line.forEach(([x, y]) => {
                    ctx.beginPath();
                    ctx.arc(x, y, 3, 0, 2 * Math.PI);
                    ctx.fill();
                });
            });
        }

        function drawPolygon() {
            const canvas = document.getElementById('polygonCanvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 外环
            const outerRing = [[40, 40], [160, 40], [160, 110], [40, 110], [40, 40]];
            // 内环(洞)
            const innerRing = [[70, 60], [130, 60], [130, 90], [70, 90], [70, 60]];
            
            // 绘制外环
            ctx.fillStyle = 'rgba(0, 150, 255, 0.3)';
            ctx.strokeStyle = '#0066cc';
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.moveTo(outerRing[0][0], outerRing[0][1]);
            outerRing.slice(1).forEach(([x, y]) => {
                ctx.lineTo(x, y);
            });
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            
            // 绘制内环(洞)
            ctx.fillStyle = '#ffffff';
            ctx.beginPath();
            ctx.moveTo(innerRing[0][0], innerRing[0][1]);
            innerRing.slice(1).forEach(([x, y]) => {
                ctx.lineTo(x, y);
            });
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
        }

        function drawMultiPolygon() {
            const canvas = document.getElementById('multipolygonCanvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            const polygons = [
                [[[30, 30], [90, 30], [90, 80], [30, 80], [30, 30]]],
                [[[110, 50], [170, 50], [170, 100], [110, 100], [110, 50]]]
            ];
            
            const colors = ['rgba(255, 100, 100, 0.3)', 'rgba(100, 255, 100, 0.3)'];
            const strokeColors = ['#cc0000', '#00cc00'];
            
            polygons.forEach((polygon, i) => {
                const ring = polygon[0]; // 只取外环
                
                ctx.fillStyle = colors[i];
                ctx.strokeStyle = strokeColors[i];
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.moveTo(ring[0][0], ring[0][1]);
                ring.slice(1).forEach(([x, y]) => {
                    ctx.lineTo(x, y);
                });
                ctx.closePath();
                ctx.fill();
                ctx.stroke();
            });
        }

        // 绘制所有示例
        drawPoint();
        drawMultiPoint();
        drawLineString();
        drawMultiLineString();
        drawPolygon();
        drawMultiPolygon();
    </script>
</body>
</html>